<?php
namespace PsiFramework\Classes\Util\Database;

import("Classes.Factory.DatabaseFactory");
import("Classes.Util.Database.DatabaseInterface.IDatabase");
import("Classes.Util.Log.Logger");
import("Classes.Util.Parameters.DAO.ParametersDAO");
import("Classes.Util.Parameters.DatabaseParameters");
import("Classes.Util.Manager.ParametersManager");

use \PsiFramework\Classes\Factory\DatabaseFactory;
use \PsiFramework\Classes\Util\Database\DatabaseInterface\IDatabase;
use \PsiFramework\Classes\Util\Database\Query\SqlTable;
use \PsiFramework\Classes\Util\Database\Query\SqlType;
use \PsiFramework\Classes\Util\Log\Logger;
use \PsiFramework\Classes\Util\Parameters\DAO\ParametersDAO;
use \PsiFramework\Classes\Util\Parameters\DatabaseParameters;
use \PsiFramework\Classes\Util\Manager\ParametersManager;

/**
 * PsiFramework
 * Php Simple Framework
 *
 * @author Rémi San
 * @version beta
 */

/**
 * The Database is used to connect to the database
 *
 * @package Util.Database
 */
abstract class Database implements IDatabase
{

    protected static $_singleton = null;

    protected /*sql_connection*/ $_connection;
    protected /*Logger*/        $_logger;

    /**
     * Empty constructor
     */
    protected function __construct()
    {
        $this->_logger = new Logger(get_class($this));
    }

    abstract public function getSqlType(SqlType $type);

    /**
     * Returns the sql request to run to know if the specified table exists
     *
     * @param     SqlTable $table
     * @return    string
     */
    abstract protected function getTableExistsQuery(
        SqlTable $table,
        /*bool*/ $view=false
    );

    /**
     * Returns the sql request to get the table structure
     *
     * @param     SqlTable $table
     * @return    string
     */
    abstract protected function getTableStructureQuery(SqlTable $table);

    /**
     * Returns true if the specified table exists
     *
     * @param     SqlTable $table
     * @return    bool
     */
    public function tableExists(SqlTable $table, /*bool*/ $view=false)
    {
        $query = $this->getTableExistsQuery($table, $view);
        $result = $this->execute($query);
        return (bool) $this->getNextResult($result, true)->table_exists ;
    }

    /**
     * Returns the table structure
     *
     * @param     SqlTable $table
     * @return    bool
     */
    public function getTableStructure(SqlTable $table)
    {
        $query = $this->getTableStructureQuery($table);
        $result = $this->execute($query);

        $columns = array();
        if ($result) {
            while (($obj = $this->getNextResult($result)) != null) {
                $column = new \stdClass();
                $column->name = $obj->name ;
                $column->default = $obj->default ;
                $column->extra = $obj->extra ;
                 
                $type = $obj->type ;
                switch(mb_strtoupper($type, 'utf-8')) {
                case 'VARCHAR' :
                case 'CHARACTER VARYING' :
                    $type = SqlType::$varchar ;
                    break ;
                case 'TEXT' :
                    $type = SqlType::$text ;
                    break ;
                case 'INT' :
                case 'INTEGER' :
                    if (mb_strtoupper($column->extra, 'utf-8')=='AUTO_INCREMENT'
                        || startsWith(
                            mb_strtoupper(
                                $column->default, 'utf-8'
                            ),
                            'NEXTVAL('
                        )
                    ) {
                        $type = SqlType::$serial ;
                    } else {
                        $type = SqlType::$integer ;
                    }
                    break ;
                case 'TINYINT' :
                case 'BOOLEAN' :
                case 'BOOL' :
                    $type = SqlType::$boolean ;
                    break ;
                case 'DATE' :
                case 'DATETIME' :
                    $type = SqlType::$dateTime ;
                    break ;
                case 'FLOAT' :
                    $type = SqlType::$float ;
                    break ;
                case 'LONG' :
                    $type = SqlType::$long ;
                    break ;
                case 'DOUBLE' :
                    $type = SqlType::$double ;
                    break ;
                }
                $column->type = $type ;
                $column->nullable =
                    (mb_strtoupper($obj->nullable, 'utf-8') == 'YES')?
                        true:false ;
                $column->length = $obj->length ;
                 
                $columns[$column->name] = $column ;
            }
        }

        return $columns ;
    }

    /**
     * Function to be called instead of the constructor.
     *
     * @return Database instance
     */
    public static function getInstance()
    {
        if (self::$_singleton==null) {
            $paramManager = ParametersManager::getInstance();
            $dbms = $paramManager->getDatabaseParameters()->getDBMS();
            self::$_singleton = DatabaseFactory::getDatabase($dbms);
        }

        return self::$_singleton;
    }
}
